﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace Medicae_Toolkit
{
    /// <summary>
    /// Form where the user utilizes most of the functionality of the application. Both Character objects
    /// and Churigeon objects are stored in this class. All functions for modifying "wounds", handling 
    /// "damage", and calculating "healing" are stored in this class.
    /// Note: The not yet implemented ExtendedCare function will have it's own form.
    /// </summary>
    public partial class Medicae : Form
    {
        public List<Character> CurrentParty;
        public List<Churigeon> Churigeons;
        Character CurrentFocus;
        Churigeon CurrentChur;

        public Medicae()
        {
            InitializeComponent();
            CurrentParty = new List<Character>();
            Churigeons = new List<Churigeon>();
        }

        /// <summary>
        /// Updates the two ListBox objects to reflect the contents of the CurrentParty and Churigeon lists.
        /// </summary>
        public void UpdateList()
        {
            comboBox1.Items.Clear();
            comboBox2.Items.Clear();
            comboBox1.Items.AddRange(CurrentParty.ToArray());
            comboBox2.Items.AddRange(Churigeons.ToArray());
        }

        /// <summary>
        /// Updates the main for with all the "stats" and "damage" of the CurrentFocus object.
        /// </summary>
        void UpdateCharacter()
        {
            charname.Text = CurrentFocus.name;
            currentwound.Text = CurrentFocus.wounds.ToString();
            healthstat.Text = CurrentFocus.DetermineStatus();
            headLb.Text = CurrentFocus.damage[(int)Character.location.Head].ToString();
            rightarmLb.Text = CurrentFocus.damage[(int)Character.location.Right_Arm].ToString();
            leftarmLb.Text = CurrentFocus.damage[(int)Character.location.Left_Arm].ToString();
            bodyLb.Text = CurrentFocus.damage[(int)Character.location.Body].ToString();
            rightlegLb.Text = CurrentFocus.damage[(int)Character.location.Right_Leg].ToString();
            leftlegLb.Text = CurrentFocus.damage[(int)Character.location.Left_Leg].ToString();
        }

        /// <summary>
        /// Opens the interface for editing the Churigeons List.
        /// </summary>
        private void editchurButton_Click(object sender, EventArgs e)
        {
            (new PartyChurigeon(this)).Show();
        }

        /// <summary>
        /// Opens the interface for editing the Party List.
        /// </summary>
        private void button4_Click(object sender, EventArgs e)
        {
            (new Party(this)).Show();
        }

        /// <summary>
        /// Whenenver the end user selects an item in the ComboBox control containing the Character objects,
        /// the Current Focus is updated and all variables related to CurrentFocus are updated.
        /// </summary>
        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboBox1.SelectedItem == null)
                return;
            CurrentFocus = (Character)comboBox1.SelectedItem;
            UpdateCharacter();
        }

         /// <summary>
        /// Whenenver the end user selects an item in the ComboBox control containing the Churigeon objects,
        /// the Current Churigeon is updated.
        /// </summary>
        private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboBox2.SelectedItem == null)
                return;

            CurrentChur = (Churigeon)comboBox2.SelectedItem;
        }

        /// <summary>
        /// This function handles the various click events for all the "+" and "-" buttons.
        /// </summary>
        private void woundmod_Click(object sender, EventArgs e)
        {
            Button clicked = (Button)sender;
            switch (clicked.Name)
            {
                case "hplus":
                    ModWound((int)Character.location.Head, 1);
                    return;
                case "raplus":
                    ModWound((int)Character.location.Right_Arm, 1);
                    return;
                case "laplus":
                    ModWound((int)Character.location.Left_Arm, 1);
                    return;
                case "bplus":
                    ModWound((int)Character.location.Body, 1);
                    return;
                case "rlplus":
                    ModWound((int)Character.location.Right_Leg, 1);
                    return;
                case "llplus":
                    ModWound((int)Character.location.Left_Leg, 1);
                    return;
                case "hminus":
                    ModWound((int)Character.location.Head, -1);
                    return;
                case "raminus":
                    ModWound((int)Character.location.Right_Arm, -1);
                    return;
                case "laminus":
                    ModWound((int)Character.location.Left_Arm, -1);
                    return;
                case "bminus":
                    ModWound((int)Character.location.Body, -1);
                    return;
                case "rlminus":
                    ModWound((int)Character.location.Right_Leg, -1);
                    return;
                case "llminus":
                    ModWound((int)Character.location.Left_Leg, -1);
                    return;
                case "wplus":
                    ModWound(-1, 1);
                    return;
                case "wminus":
                    ModWound(-1, -1);
                    return;
            }
        }

        /// <summary>
        /// ModWound handles all changes in damage and wounds.
        /// </summary>
        /// <param name="local"> The location of the change, with -1 only applying to wounds.</param>
        /// <param name="mod"> The amount of change. Damage to a body part are changed by this much,
        /// while the wounds as a whole are changed by the negative of mod.</param>
        /// <param name="medicare"> Whether or not this is Extended or Natural healing</param>
        private void ModWound(int local, int mod, bool medicare = false)
        {
            // Note: Thanks for a few talents, it *is* possible to go over the wounds cap, if unlikely
            // In these cases, first aid and extended care will only mod up to the cap.

            if (CurrentFocus == null) // If they hit a plus or minus before actually selecting a character
                return;

            if (local == -1) // Directly modifying wounds
            {         
                CurrentParty.Remove(CurrentFocus);
                if (medicare) // For Extended, First Aid, and Natural Healing
                {
                    // Yes, this can result in a negative amount if
                    // target has more wounds that his max wounds.
                    if( CurrentFocus.wounds + mod > CurrentFocus.woundcap )
                        mod = CurrentFocus.woundcap - CurrentFocus.wounds;

                    // Reset damage to all body parts
                    for (int i = 0; i < (int)Character.location.End; i++)
                    {
                        CurrentFocus.damage[i] = 0;
                    }
                    CurrentFocus.wounds += mod;
                }
                else
                    CurrentFocus.wounds += mod;
                CurrentParty.Add(CurrentFocus);
                UpdateCharacter();
                return;
            }

            if (CurrentFocus.damage[local] + mod < 0) // You can't have negative wounds on a body part
                return;

            CurrentParty.Remove(CurrentFocus);
            CurrentFocus.damage[local] += mod;
            CurrentFocus.wounds -= mod;
            CurrentParty.Add(CurrentFocus);
            UpdateCharacter();
        }

        /// <summary>
        /// This function controls "Natural Healing" as determined by Dark Heresy rules.
        /// </summary>
        private void nathealButton_Click(object sender, EventArgs e)
        {
            if (CurrentFocus.DetermineStatus() == "Lightly Wounded")
            {
                if (MessageBox.Show("Does the target spend a day resting?", "Resting", MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    ModWound(-1, 1 + CurrentFocus.ToughnessBonus, true);
                }
                else
                    ModWound(-1, 1, true);
            }
            if (CurrentFocus.DetermineStatus() == "Heavily Wounded")
            {
                if (MessageBox.Show("Does the target spend a week resting?", "Resting", MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    ModWound(-1, 1 + CurrentFocus.ToughnessBonus, true);
                }
                else
                    ModWound(-1, 1, true);
            }
            return; // Critical Damage does not naturally go away without medical attention
        }

        /// <summary>
        /// This function controls "First Aid" as determined by Dark Heresy rules.
        /// </summary>
        private void firstaidButton_Click(object sender, EventArgs e)
        {
            if (CurrentFocus == null || rollBox.Text == "" || localCombi.SelectedItem == null)
                return; // Must have a roll or focus to use first aid
            if (CurrentChur == null)
            {
                MessageBox.Show("Please Select or Create a Churigeon.", "Churigeon");
                return;
            }
            int roll;
            try
            {
                roll = Convert.ToInt32(rollBox.Text);
            }
            catch (FormatException fe)
            {
                MessageBox.Show("Invalid roll!", "Invalid Roll");
                return;
            }

            if (medkit.Checked) // Medkit's give +20 to Medicae Rolls on first aid
                roll += 20;

            if (roll > 99) // Critical fails always count as failing.
                roll = 99; 

            int degrees = CurrentChur.GetDegrees(roll, 0);
            int dmgmod = 0;
            if (degrees <= -3)
            {
                dmgmod = -1;
            }
            else if (degrees == 0)
            {
                dmgmod = Convert.ToInt32(Math.Floor((double)CurrentChur.IntellectBonus * (1.0/3.0)));
            }
            else if (degrees == 1)
            {
                dmgmod = Convert.ToInt32(Math.Floor((double)CurrentChur.IntellectBonus * 0.5));
            }
            else if (degrees == 2)
            {
                dmgmod = CurrentChur.IntellectBonus;
            }
            else if (degrees >= 3)
            {
                dmgmod = Convert.ToInt32(Math.Floor((double)CurrentChur.IntellectBonus * 1.5));
            }

            switch (localCombi.SelectedItem.ToString())
            {
                case "Body":
                    if (CurrentFocus.damage[(int)Character.location.Body] - dmgmod < 0)
                        ModWound((int)Character.location.Body, -(CurrentFocus.damage[(int)Character.location.Body]));
                    else
                        ModWound((int)Character.location.Body, -dmgmod);
                    return;
                case "Head":
                 if (CurrentFocus.damage[(int)Character.location.Head] - dmgmod < 0)
                     ModWound((int)Character.location.Head, -(CurrentFocus.damage[(int)Character.location.Head]));
                    else
                     ModWound((int)Character.location.Head, -dmgmod);
                    return;
                case "Right Arm":
                 if (CurrentFocus.damage[(int)Character.location.Right_Arm] - dmgmod < 0)
                        ModWound((int)Character.location.Right_Arm, -(CurrentFocus.damage[(int)Character.location.Right_Arm]));
                    else
                        ModWound((int)Character.location.Right_Arm, -dmgmod);
                    return;
                case "Left Arm":
                 if (CurrentFocus.damage[(int)Character.location.Left_Arm] - dmgmod < 0)
                     ModWound((int)Character.location.Left_Arm, -(CurrentFocus.damage[(int)Character.location.Left_Arm]));
                    else
                     ModWound((int)Character.location.Left_Arm, -dmgmod);
                    return;
                case "Right Leg":
                 if (CurrentFocus.damage[(int)Character.location.Right_Leg] - dmgmod < 0)
                     ModWound((int)Character.location.Right_Leg, -(CurrentFocus.damage[(int)Character.location.Right_Leg]));
                    else
                     ModWound((int)Character.location.Right_Leg, -dmgmod);
                    return;
                case "Left Leg":
                  if (CurrentFocus.damage[(int)Character.location.Left_Leg] - dmgmod < 0)
                      ModWound((int)Character.location.Left_Leg, -(CurrentFocus.damage[(int)Character.location.Left_Leg]));
                    else
                      ModWound((int)Character.location.Left_Leg, -dmgmod);
                   return;
            }
        }

        /// <summary>
        /// This functions controls "Extended Care" as determined by Dark Heresy rules.
        /// Not currently implemented.
        /// </summary>
        private void exthealButton_Click(object sender, EventArgs e)
        {
            MessageBox.Show("This Feature is not currently implemented!", "Error");
        }

         /// <summary>
        /// Opens the Save file dialong for saving "dhparty" files.
        /// </summary>
        private void saveButton_Click(object sender, EventArgs e)
        {
            savepartyDialog.ShowDialog();
        }

        /// <summary>
        /// Opens the Load file dialong for opening saved "dhparty" files.
        /// </summary>
        private void button5_Click(object sender, EventArgs e)
        {
            partyDialog.ShowDialog();
        }

        /// <summary>
        /// Controls Seralization and Saving of "dhparty" files.
        /// </summary>
        private void savepartyDialog_FileOk(object sender, CancelEventArgs e)
        {
            SerialContainer S = new SerialContainer();
            S.Serial1 = CurrentParty;
            S.Serial2 = Churigeons;

            try
            {
                Stream save = File.Open(savepartyDialog.FileName, FileMode.Create);
                BinaryFormatter bf = new BinaryFormatter();
                bf.Serialize(save, S);
                save.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error: Report to Richard Simpson");
            }

        }

        /// <summary>
        /// Controls Deseralization and loading of "dhparty" files.
        /// </summary>
        private void partyDialog_FileOk(object sender, CancelEventArgs e)
        {
            SerialContainer S;
            Stream save = File.Open(partyDialog.FileName, FileMode.Open);
            BinaryFormatter bf = new BinaryFormatter();
            S = (SerialContainer)bf.Deserialize(save);
            save.Close();

            CurrentParty = S.Serial1;
            Churigeons = S.Serial2;
            UpdateList();
        }
    }
}
